/*
 * Copyright 2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package groovyx.gaelyk.obgaektify

import com.googlecode.objectify.Objectify
import com.googlecode.objectify.ObjectifyFactory
import com.googlecode.objectify.ObjectifyService
import com.googlecode.objectify.util.DAOBase

/**
 * Exposes all the methods of com.googlecode.objectify.helper.DAOBase and com.googlecode.objectify.helper.ObjectifyImpl
 *
 * @author Jeremy Brown
 */
class ObgaektifyDAO extends DAOBase
{
	@Delegate Objectify objectify
	@Delegate ObjectifyFactory factory
	
	ObgaektifyDAO()
	{
		factory = ObjectifyService.factory()
		objectify = factory.begin()
	}
	
	
	/*
		e.g.
		dao.withTransaction{t->
			def object = t.get(someKey)
			object.someProperty = newValue
			t.put(object)
		}
	*/
	public void withTransaction(Closure transactionClosure)
	{
		Objectify objectifyTransaction = ObjectifyService.factory().beginTransaction()
		try
		{
			// pass the transaction as single parameter of the closure
			transactionClosure(objectifyTransaction)
			// commit the transaction if the closure executed without throwing an exception
			objectifyTransaction.getTxn().commit()
		}
		finally
		{
			// rollback on error
			if(objectifyTransaction.getTxn().isActive())
			{
				objectifyTransaction.getTxn().rollback()
			}
		}
	}
	
	//Try to run a closure, and if it throws an uncaught exception, 
	//retry until it succeeds or until the number of retries is exhausted.
	//In your closure, you should catch any exceptions that you don't want to
	//trigger a retry (like NumberFormatException, NullPointerException, etc.)
	public void withTransactionRetry(int retries, Closure transactionClosure)
	{
		//0 retries means try only once
		//1 retries means try at most twice
		//2 retries means try at most thrice
		//etc.
		if(retries < 0){retries = 0}
		boolean success = false
		int tries = 0
		def lastException = null
		
		while(!success && tries <= retries)
		{
			success = true //after the loop, exit, unless there is an Exception or a rollback
			lastException = null
			
			Objectify objectifyTransaction = ObjectifyService.factory().beginTransaction()
			try
			{
				// pass the transaction as single parameter of the closure
				transactionClosure(objectifyTransaction)
				// commit the transaction if the closure executed without throwing an exception
				objectifyTransaction.getTxn().commit()
			}
			catch(Exception e)
			{
				success = false
				lastException = e
			}
			finally
			{
				// rollback on error
				if(objectifyTransaction.getTxn().isActive())
				{
					objectifyTransaction.getTxn().rollback()
					success = false
				}
			}
			tries++
		}
		if(lastException)
		{
			throw lastException
		}
	}
}